package com.fary.boot.logging;

import com.fary.util.ClassUtils;
import com.fary.util.StringUtils;

import java.lang.reflect.Constructor;
import java.util.*;

public abstract class LoggingSystem {

    // 系统属性名,用该属性来配置使用哪个LoggingSystem的实现
    public static final String SYSTEM_PROPERTY = LoggingSystem.class.getName();

    // 如果SYSTEM_PROPERTY对应的属性值为none,则意味着不希望使用LoggingSystem-->此时获得的是NoOpLoggingSystem,什么都不会进行打印
    public static final String NONE = "none";

    // 根节点logger的名字,LoggingSystem的实现应该使其作为根节点logger的名字,而不去管底层的实现
    public static final String ROOT_LOGGER_NAME = "ROOT";

    // 默认支持的类,key-->logger框架的类,value--> 对应的LoggingSystem 实现
    private static final Map<String, String> SYSTEMS;

    static {
        Map<String, String> systems = new LinkedHashMap<>();
        systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");
        systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
                "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
        systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
        SYSTEMS = Collections.unmodifiableMap(systems);
    }

    // 重新设置日志系统以减少输出,该方法应该在initialize方法前调用以减少日志的噪声直到日志系统初始化完毕,
    public abstract void beforeInitialize();

    /**
     * 初始化日志系统,
     */
    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
    }

    /**
     * 清除资源
     */
    public void cleanUp() {
    }

    /**
     * 当jvm退出时来处理日志系统的关闭,默认返回null,表明不需要进行处理
     */
    public Runnable getShutdownHandler() {
        return null;
    }

    /**
     * 返回该日志系统支持的日志级别,默认是所有都支持
     */
    public Set<LogLevel> getSupportedLogLevels() {
        return EnumSet.allOf(LogLevel.class);
    }

    /**
     * 为给定的logger设置日志级别
     */
    public void setLogLevel(String loggerName, LogLevel level) {
        throw new UnsupportedOperationException("Unable to set log level");
    }

    /**
     * 返回当前LoggingSystem中的所有的LoggerConfiguration
     */
    public List<LoggerConfiguration> getLoggerConfigurations() {
        throw new UnsupportedOperationException("Unable to get logger configurations");
    }

    /**
     * 返回给定的logger所对应的LoggerConfiguration
     */
    public LoggerConfiguration getLoggerConfiguration(String loggerName) {
        throw new UnsupportedOperationException("Unable to get logger configuration");
    }

    /**
     * 获得LoggingSystem.
     */
    public static LoggingSystem get(ClassLoader classLoader) {
        // 1. 获得环境变量中key为org.springframework.boot.logging.LoggingSystem所对应的值
        String loggingSystem = System.getProperty(SYSTEM_PROPERTY);
        // 2. 如果配置的值为none,则返回NoOpLoggingSystem,否则,对其进行实例化
        if (StringUtils.hasLength(loggingSystem)) {
            if (NONE.equals(loggingSystem)) {
                return new NoOpLoggingSystem();
            }
            return get(classLoader, loggingSystem);
        }
        // 3. 否则,遍历SYSTEMS,如果在当前类路径下存在所配置的类,则直接实例化,返回,默认使用的是LogbackLoggingSystem
        return SYSTEMS.entrySet().stream().filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader))
                .map((entry) -> get(classLoader, entry.getValue())).findFirst()
                .orElseThrow(() -> new IllegalStateException("No suitable logging system located"));
    }

    /**
     * 实例化LoggingSystem.
     */
    private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) {
        try {
            Class<?> systemClass = ClassUtils.forName(loggingSystemClass, classLoader);
            Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
            constructor.setAccessible(true);
            return (LoggingSystem) constructor.newInstance(classLoader);
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }

    /**
     * {@link LoggingSystem} that does nothing.
     */
    static class NoOpLoggingSystem extends LoggingSystem {

        @Override
        public void beforeInitialize() {

        }

        @Override
        public void setLogLevel(String loggerName, LogLevel level) {

        }

        @Override
        public List<LoggerConfiguration> getLoggerConfigurations() {
            return Collections.emptyList();
        }

        @Override
        public LoggerConfiguration getLoggerConfiguration(String loggerName) {
            return null;
        }

    }

}